package com.ncell;

import com.gooddies.swing.hComboBox;
import com.ncell.cell.I;
import com.ncell.cell.P;
import com.ncell.cell.PIUnit;
import com.ncell.cell.UnitProcessor;
import com.ncell.gui.PIUnitCanvas;
import com.swingson.Swingson;
import com.swingson.SwingsonGuiBuilder;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.Locale;
import java.util.Random;
import java.util.Scanner;
import javax.swing.*;

public class NCellMain {

    private static class MyFrame extends JFrame {

        private PIUnitCanvas iuCanvas;
        private final hComboBox<ComboItem> patternCB = new hComboBox<>();
        private UnitProcessor processor;
        private Timer timer;
        private JLabel info;

        public MyFrame() throws HeadlessException {
            setTitle("Emulator");
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            init();
            pack();
            setLocationRelativeTo(null);
            setVisible(true);
        }

        private void init() {
            final PIUnit unit = new PIUnit(10, 10, 0);
            processor = new UnitProcessor();
            processor.setPUnit(unit);
            iuCanvas = new PIUnitCanvas();
            iuCanvas.setFocusable(false);
            iuCanvas.setFocusable(true);
            iuCanvas.setPIUnit(unit);
            MouseAdapter ma = new MouseAdapter() {
                boolean pressed = false;
                float activationToSet = 0;
                private P lastP;

                @Override
                public void mousePressed(MouseEvent e) {
                    if (e.getButton() == 1) {
                        P p = iuCanvas.getPByXy(e.getX(), e.getY());
                        lastP = p;
                        if (p != null) {
                            p.setActivation(p.getActivation() > 0.5 ? 0 : 1);
                        }

                        pressed = true;
                    }

                    repaint();
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    pressed = false;
                    iuCanvas.requestFocus();
                    repaint();
                }

                @Override
                public void mouseMoved(MouseEvent e) {
                    if (!pressed) {
                        I i = iuCanvas.getIByXy(e.getX(), e.getY());
                        if (i != null) {
                            info.setText(i.toString());
                        } else {
                            P p = iuCanvas.getPByXy(e.getX(), e.getY());
                            if (p != null) {
                                info.setText(p.toString());
                            }
                        }
                    }
                }

                @Override
                public void mouseDragged(MouseEvent e) {
                    if (pressed) {
                        P p = iuCanvas.getPByXy(e.getX(), e.getY());
                        if (p != lastP) {
                            if (p != null) {
                                lastP = p;
                                p.setActivation(p.getActivation() > 0.5 ? 0 : 1);
                                repaint();
                            }
                        }
                    }
                }
            };

            iuCanvas.addMouseListener(ma);
            iuCanvas.addMouseMotionListener(ma);
            patternCB.setRenderer(new ComboBoxRenderer());
            loadData();
            SwingsonGuiBuilder.createGuiFromJsonInPackage(this);
        }

        public void onPatternChanged() {
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    try (Swingson.UserInputEnabler enabler = Swingson.disableUserInput()) {
                        if (patternCB.getSelectedItem() == null) {
                            return;
                        }

                        ComboItem item = patternCB.getSelectedItem();
                        float[] values = item.getValues();
                        int index = -1;
                        for (P p : iuCanvas.getUnit().getPs()) {
                            index++;
                            p.setActivation(values[index]);
                        }

                        processor.processStep();
                        iuCanvas.repaint();
                    }
                }
            });
        }

        public void removePattern() {
            try (Swingson.UserInputEnabler enabler = Swingson.disableUserInput()) {
                if (patternCB.getSelectedItem() == null) {
                    return;
                }

                int index = patternCB.getSelectedIndex();
                if (index == -1) {
                    return;
                }

                patternCB.removeItemAt(index);
                if (patternCB.getItemCount() == 0) {
                    patternCB.setSelectedItem(null);
                }
            }

            try {
                storeData();
            } catch (FileNotFoundException ex) {
                ex.printStackTrace();
            }
        }

        public void addPattern() {
            float[] values = new float[iuCanvas.getUnit().getPs().size()];
            int index = -1;
            for (P p : iuCanvas.getUnit().getPs()) {
                index++;
                values[index] = p.getActivation();
            }

            try (Swingson.UserInputEnabler enabler = Swingson.disableUserInput()) {
                ComboItem item = new ComboItem(values, createIconForValue(values, iuCanvas.getUnit().getWidth(), iuCanvas.getUnit().getHeight()));
                patternCB.addItem(item);
                patternCB.setSelectedItem(item);
            }
            try {
                storeData();
            } catch (FileNotFoundException ex) {
                ex.printStackTrace();
            }
        }

        private void storeData() throws FileNotFoundException {
            try (PrintStream stream = new PrintStream(new File("./stored.txt"))) {
                for (ComboItem item : patternCB.getItemsAsList()) {
                    for (int i = 0; i < item.getValues().length; i++) {
                        if (i != 0) {
                            stream.print(",");
                        }

                        stream.printf("%.4f", item.getValues()[i]);
                    }

                    stream.println();
                }
            }
        }

        private void loadData() {
            File file = new File("./stored.txt");
            if (!file.exists()) {
                return;
            }

            try (Swingson.UserInputEnabler enabler = Swingson.disableUserInput()) {
                Scanner scanner;
                try {
                    scanner = new Scanner(file);
                    while (scanner.hasNextLine()) {
                        String line = scanner.nextLine();
                        if (line.trim().isEmpty()) {
                            continue;
                        }

                        String[] strvalues = line.trim().split(",");
                        float[] values = new float[strvalues.length];
                        for (int i = 0; i < strvalues.length; i++) {
                            values[i] = Float.parseFloat(strvalues[i]);
                        }
                        Icon icon = createIconForValue(values, iuCanvas.getUnit().getWidth(), iuCanvas.getUnit().getHeight());
                        ComboItem item = new ComboItem(values, icon);
                        patternCB.addItem(item);
                    }
                } catch (FileNotFoundException ex) {
                    ex.printStackTrace();
                }
            }
            if (patternCB.getItemCount() > 0) {
                patternCB.setSelectedIndex(0);
                onPatternChanged();
            }
        }

        public void onProcess() {
            processor.processStep();
            iuCanvas.repaint();
        }

        public void onRandom() {
            if (timer != null) {
                timer.stop();
                timer = null;
                return;
            }

            timer = new Timer(10, new ActionListener() {
                Random r = new Random();

                @Override
                public void actionPerformed(ActionEvent e) {
                    int maxNumber = patternCB.getItemCount();
                    int item = r.nextInt(maxNumber);
                    if (item >= 0) {
                        patternCB.setSelectedIndex(item);
                        onPatternChanged();
                    }
                }
            });

            timer.setRepeats(true);
            timer.start();
        }
    }

    private static Icon createIconForValue(float[] value, int width, int height) {
        int scale = 3;
        int w = width;
        int h = height;
        BufferedImage image = new BufferedImage(w * scale, h * scale, BufferedImage.TYPE_INT_ARGB);
        Graphics gr = image.getGraphics();
        gr.setColor(Color.WHITE);
        gr.fillRect(0, 0, w * scale, h * scale);
        gr.setColor(Color.BLACK);
        int index = 0;
        for (int j = 0; j < h; j++) {
            for (int i = 0; i < w; i++, index++) {
                if (value[index] > 0.5f) {
                    gr.fillRect(i * scale, j * scale, scale, scale);
                }
            }
        }
        gr.dispose();
        return new ImageIcon(image);
    }

    public static void main(String[] args) {
        Locale.setDefault(new Locale("en", "US"));
        SwingsonGuiBuilder.setWindowsLookAndFeel();
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                MyFrame frame = new MyFrame();
            }
        });
    }
}
